//
//  Copyright (C) 2010 Vadim Ushakov
// 
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU General Public License as published by the Free
//  Software Foundation; either version 2 of the License, or (at your option)
//  any later version.
// 
//  This program is distributed in the hope that it will be useful, but
//  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
//  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
//  for more details.
//  
//  You should have received a copy of the GNU General Public License along
//  with this program; if not, write to the Free Software Foundation, Inc.,
//  59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//


#include "kernelCircularBuffer.h"
#include "kernelMalloc.h"
#include "core1/kernelMultitasker.h"
#include <stdlib.h>
#include <string.h>
#include <sys/errors.h>


int kernelCircularBufferAllocate(kernelCircularBuffer* buffer, int size)
{
	if (!buffer)
		return ERR_NULLPARAMETER;

	if (size < 1)
		return ERR_INVALID;

	buffer->data = kernelMalloc(size);
	if (!buffer->data)
		return ERR_MEMORY;

	buffer->size = size;
	buffer->readerPosition = 0;
	buffer->writerPosition = 0;

	return 0;
}

int kernelCircularBufferFree(kernelCircularBuffer* buffer)
{
	if (!buffer)
		return ERR_NULLPARAMETER;

	kernelFree(buffer->data);
	buffer->data = 0;
	buffer->size = 0;
	buffer->readerPosition = 0;
	buffer->writerPosition = 0;

	return 0;
}

int kernelCircularBufferWrite(kernelCircularBuffer* buffer, void* data, int size)
{
	if (!buffer)
		return ERR_NULLPARAMETER;

	if (!buffer->data)
		return ERR_INVALID;

	if (size >= buffer->size)
	{
		if (buffer->overrun < UINT_MAX)
			buffer->overrun++;
		return ERR_INVALID;
	}

	if (kernelCircularBufferGetFreeSize(buffer) < size)
	{
		if (buffer->overrun < UINT_MAX)
			buffer->overrun++;
		return ERR_NOFREE;
	}

	int pos = buffer->writerPosition;
	int nextPos = pos + size;
	if (nextPos >= buffer->size)
	{
		int size1 = buffer->size - pos;
		memcpy(((char*)buffer->data) + pos, data, size1);
		data = ((char*)data) + size1;
		size -= size1;
		pos = 0;
		nextPos = pos + size;
	}
	memcpy(((char*)buffer->data) + pos, data, size);

	buffer->writerPosition = nextPos;

	if (buffer->fastNotifyProcessId > 0)
	{
		kernelMultitaskerSendFastNotify(buffer->fastNotifyProcessId);
	}

	return 0;
}

int kernelCircularBufferRead(kernelCircularBuffer* buffer, void* data, int size)
{
	if (!buffer)
		return ERR_NULLPARAMETER;

	if (!buffer->data)
		return ERR_INVALID;

	if (size >= buffer->size)
		return ERR_INVALID;

	if (kernelCircularBufferGetDataSize(buffer) < size)
		return ERR_NODATA;

	int pos = buffer->readerPosition;
	int nextPos = pos + size;
	if (nextPos >= buffer->size)
	{
		int size1 = buffer->size - pos;
		memcpy(data, ((char*)buffer->data) + pos, size1);
		data = ((char*)data) + size1;
		size -= size1;
		pos = 0;
		nextPos = pos + size;
	}
	memcpy(data, ((char*)buffer->data) + pos, size);

	buffer->readerPosition = nextPos;

	return 0;
}

int kernelCircularBufferPurge(kernelCircularBuffer* buffer)
{
	if (!buffer)
		return ERR_NULLPARAMETER;

	if (!buffer->data)
		return ERR_INVALID;

	buffer->readerPosition = buffer->writerPosition;

	return 0;
}

int kernelCircularBufferSetFastNotifyProcessId(kernelCircularBuffer* buffer, int processId)
{
	if (!buffer)
		return ERR_NULLPARAMETER;

	buffer->fastNotifyProcessId = processId;

	return 0;
}

unsigned kernelCircularBufferGetOverrun(kernelCircularBuffer* buffer)
{
	return (buffer->overrun);
}

unsigned kernelCircularBufferDecrementOverrun(kernelCircularBuffer* buffer)
{
	if (buffer->overrun)
		buffer->overrun--;

	return (buffer->overrun);
}

int kernelCircularBufferGetBufferSize(kernelCircularBuffer* buffer)
{
	if (!buffer)
		return ERR_NULLPARAMETER;

	if (!buffer->data)
		return ERR_INVALID;

	return buffer->size;
}

int kernelCircularBufferGetDataSize(kernelCircularBuffer* buffer)
{
	if (!buffer)
		return ERR_NULLPARAMETER;

	if (!buffer->data)
		return ERR_INVALID;

	int diff = buffer->writerPosition - buffer->readerPosition;
	if (diff < 0)
		diff += buffer->size;
	return diff;
}

int kernelCircularBufferGetFreeSize(kernelCircularBuffer* buffer)
{
	if (!buffer)
		return ERR_NULLPARAMETER;

	if (!buffer->data)
		return ERR_INVALID;

	return buffer->size - kernelCircularBufferGetDataSize(buffer) - 1;
}

